package dao.ebean;

import com.avaje.ebean.Ebean;
import com.avaje.ebean.Query;
import com.avaje.ebean.Update;
import dao.common.CategoryRepository;
import dao.exceptions.CreateEntityException;
import dao.exceptions.DeleteEntityException;
import models.Category;
import models.Shop;

import java.util.List;

public class CategoryRepositoryImpl implements CategoryRepository {
    public void add(Category category) {
        try {
            Shop shop = category.shop;
            long categoryCount = shop.categories.size();
            if (categoryCount == 0) {
                category.leftValue = 1;
                category.rightValue = 2;
                Ebean.save(category);
                shop.categories.add(category);
                //shop.save();
            } else {
                if (category.parentId == 0) {
                    throw new CreateEntityException("ParentCategoryId must be set");
                }
                makeShiftForCreate(category);
                Ebean.save(category);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new CreateEntityException(ex.getMessage());
        }
    }

    public List<Category> getAll(long shopId) {
        Query<Category> categoriesQuery = Ebean.find(Category.class)
                .where("shop.alias = :shopId")
                .setParameter("shopId", shopId);
        return categoriesQuery.findList();
    }

    public Category getById(long id, long shopId) {
        Query<Category> categoryQuery = Ebean.find(Category.class)
                .where("shop.alias = :shopId and alias = :categoryId")
                .setParameter("shopId", shopId)
                .setParameter("categoryId", id);
        return categoryQuery.findUnique();
    }

    public void remove(long id, long shopId) {
        try {
            Category category = Ebean.find(Category.class, id);
            long myLeft = category.leftValue;
            long myRight = category.rightValue;
            long myWidth = myRight - myLeft + 1;
            String deleteQueryString = "delete from Category c " +
                    "where c.leftValue between :myLeft and :myRight and c.shop.alias = :shopId";
            Update<Category> deleteQuery = Ebean.createUpdate(Category.class, deleteQueryString)
                    .setParameter("myLeft", myLeft)
                    .setParameter("myRight", myRight)
                    .setParameter("shopId", shopId);
            deleteQuery.execute();

            String updateRightValueString = 
                    "update Category c set c.rightValue = c.rightValue - :myWidth " +
                            "where c.rightValue > :myRight and c.shop.alias = :shopId";
            Update<Category> updateRightValueQuery = Ebean.createUpdate(Category.class, 
                    updateRightValueString)
                    .setParameter("myWidth", myWidth)
                    .setParameter("myRight", myRight)
                    .setParameter("shopId", shopId);
            updateRightValueQuery.execute();

            String updateLeftValueString = "update Category c " +
                    "set c.leftValue = c.leftValue - :myWidth " +
                    "where c.leftValue > :myRight and c.shop.alias = :shopId";
            Update<Category> updateLeftValueQuery = Ebean.createUpdate(Category.class,
                    updateLeftValueString)
                    .setParameter("myWidth", myWidth)
                    .setParameter("myRight", myRight)
                    .setParameter("shopId", shopId);
            updateLeftValueQuery.execute();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new DeleteEntityException(ex.getMessage());
        }
    }

    private void makeShiftForCreate(Category category) {
        Category parentCategory = Ebean.find(Category.class, category.parentId);
        long rightValue = parentCategory.rightValue - 1;
        String updateRightPartString = "update Category set rightValue = rightValue + 2 where rightValue > :rightValue;";
        Update<Category> updateRightPart = Ebean.createUpdate(Category.class, updateRightPartString);
        updateRightPart.setParameter("rightValue", rightValue);
        updateRightPart.execute();
        String updateLeftPartString = "update Category set leftValue = leftValue + 2 where leftValue > :rightValue;";
        Update<Category> updateLeftPart = Ebean.createUpdate(Category.class, updateLeftPartString);
        updateLeftPart.setParameter("rightValue", rightValue);
        updateLeftPart.execute();
        category.leftValue = rightValue + 1;
        category.rightValue = rightValue + 2;
    }
}
